package com.xh.easyretry.handler;


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

import javax.el.MethodNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

public class ClassHandler implements ApplicationContextAware {

    private static final ClassHandler CLASS_HANDLER = new ClassHandler();

    private ApplicationContext applicationContext;


    public static ClassHandler getInstance() {
        return CLASS_HANDLER;
    }

    /**
     * 获取方法对象
     *  方法标签。格式：serviceName.methodName / .methodName / methodName  若输入：
     *      serviceName.methodName：则从容器中获取serviceName下的methodName方法
     *      .methodName：则从容器获取指定clazz下的methodName方法
     *      methodName：则从容器获取指定clazz下的methodName方法
     *
     * @param clazz       若methodLabel未指定方法所属类className，则使用当前clazz作为方法所属类获取方法对象
     * @param methodLabel 方法标签。格式：serviceName.methodName / .methodName / methodName
     * @param argTypes    方法参数类型数组
     * @return 方法对象
     * @throws NoSuchMethodException 未找到方法时抛出当前异常
     * @throws MethodNotFoundException 解析methodLabel失败
     */
    public Method getMethod(Class<?> clazz, String methodLabel, Class<?>[] argTypes)
            throws NoSuchMethodException, MethodNotFoundException {
        Class<?> beanClass = null;
        String methodName = null;
        String[] segments = methodLabel.split("\\.");
        if (Objects.equals(segments.length, 1)) {
            beanClass = clazz;
            methodName = segments[0];
        } else if (Objects.equals(segments.length, 2)) {
            String className = segments[0];
            if (StringUtils.hasText(className)) {
                beanClass = getClassByName(className);
            } else {
                beanClass = clazz;
            }
            methodName = segments[1];
        }
        if (Objects.isNull(beanClass) || !StringUtils.hasText(methodName)) {
            throw new MethodNotFoundException("ClassHandler_getMethod method not found");
        }

        return beanClass.getMethod(methodName, argTypes);
    }

    /**
     * 执行方法
     *
     * @param method 方法对象
     * @param bean   方法所属类对象
     * @param args   方法参数数组
     * @return Object 方法执行返回值
     */
    public Object processMethod(Method method, Object bean, Object[] args)
            throws InvocationTargetException, IllegalAccessException {
        if (Objects.isNull(method)) {
            throw new IllegalArgumentException("method is null");
        }
        return method.invoke(bean, args);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private Class<?> getClassByName(String name) {
        return applicationContext.getType(name);
    }
}
